Go の Context でキャンセル処理
ちょっとずつ処理して打ち切るパターン
ループ内でたくさん処理する
外からもらった context が Cancel 済みならそこで諦める
code:cancelInLoop.go
func isDone(ctx context.Context) bool {
select {
case <-ctx.Done():
return true
default:
return false
}
}
func doInLoop(ctx context.Context, t time.Duration) error {
wait := time.Duration(0)
for wait <= t {
// なんか処理
time.Sleep(100 * time.Millisecond)
wait += 100 * time.Millisecond
// ループごとにチェックしてキャンセルされてたら打ち切る
if isDone(ctx) {
// db なら rollback したり
fmt.Printf("%+v\n", "cancelled in doInLoop")
return ctx.Err()
}
}
fmt.Printf("%+v\n", "done")
return nil
}
もっと簡単に
code:timedout.go
for _, i := range items {
select {
case <-ctx.Done():
return
default:
process(i)
}
}
処理しつつ Timeout を待つパターン
doSomething に ctx 渡せたり中断できる処理がないとあまり意味ない
特に http なら TimeoutHandler で済ませるほうがよさそう
こういう雰囲気で待ってる
code:doTimeout.go
ch := make(chan error, 1)
ctx := req.Context() // http.Request など
go func() {
err := doSomething()
if err != nil {
return err
}
ch <- err
}()
// 処理が終わるか timeout に達するか待つ
select {
case err := <- ch:
// done doSomething()
return err
case <- ctx.Done()
return ctx.Err()
}